#include "../IO Interface/DataManager.h"
#include "../OptimizerFineTuning/OptimizerFineTuning.h"
#include <queue>
using std::endl;
using std::vector;
using std::pair;
using std::min;
using std::queue;
using std::cout;
typedef pair<int, int> Edge;

#define CMAKE
//#define VISUALSTUDIO
//#define SUBMIT

const int MAXN = 1000000;
int siteNum, clientNum, T;
vector<pair<int, int> > graph;
vector<int> demands, limits;
// InputManager inputManager("data\\");
// OutputManager outputManager;


#ifdef CMAKE
DataManager datamanager("../data-small");
#endif
#ifdef VISUALSTUDIO
DataManager datamanager("../../../data");
#endif
#ifdef SUBMIT
DataManager datamanager("/data");
#endif


int SOURCE_PORT, TARGET_PORT;
int n, depth[MAXN];
int h[MAXN], cur[MAXN], nx[MAXN << 1], to[MAXN << 1], cap[MAXN << 1], cnt=1;
void addEdge(int _from, int _to, int _flow){
    cnt ++;
    to[cnt] = _to;
    nx[cnt] = h[_from];
    h[_from] = cnt;
    cap[cnt] = _flow;
    cnt++;
    to[cnt] = _from;
    nx[cnt] = h[_to];
    h[_to] = cnt;
    cap[cnt] = 0;
}

bool bfs(int S, int T){
    for(int i=1;i<=n;i++){
        depth[i] = -1;
    }
    depth[S] = 0;
    queue<int> Q;
    Q.push(S);    
    while(Q.size()){
        int x = Q.front();
        Q.pop();
        for(int i=h[x];i;i=nx[i]){
            if(depth[to[i]] == -1 && cap[i] > 0){
                depth[to[i]] = 1 + depth[x];
                Q.push(to[i]);
            }
        }
    }
    return (depth[T] != -1);
}

int dfs(int T, int x, int a){
    if(x == T) return a;
    int flow = a, f;
    for(int i=cur[x];i;i=nx[i]){
        if(depth[to[i]] == depth[x] + 1 && cap[i] > 0){
            f = dfs(T, to[i], min(cap[i], a));
            cap[i] -= f;
            cap[i ^ 1] += f;
            a -= f;
            if(cap[i] > 0){
                cur[x] = i;
            }
            if(a <= 0) return flow;
        }
    }
    if(a != 0) depth[x] = -1;
    return flow - a;
}

int dinic(int S, int T){
    int flow = 0, f;
    while(bfs(S, T)){
        // cout << "abab\n";
        for(int i=1;i<=n;i++)
            cur[i] = h[i];
        while((f = dfs(T, S, 0x3f3f3f3f)) != 0){
            flow += f;
        }
    }
    return flow;
}
void clear(){
    for(int i=0;i<=n;i++){
        h[i] = 0;
    }
    cnt = 1;
}
void outputAns(int ti){
    // cout << "setTime ";
    datamanager.setTime(ti);
    // cout << "setTime end\n";
    for(int i=1;i<=clientNum;i++){
        // cout << "setClient ";
        datamanager.setClient(i);
        // cout << "setClient end\n";
        int nodeID = siteNum + i;
        for(int j=h[nodeID];j;j=nx[j]){
            // cout << j << " " << to[j] <<" " << siteNum<< " " << cap[j]<< endl;
            if(to[j] <= siteNum && cap[j] > 0){
               // cout << to[j] << " " << i <<" "<< cap[j] << endl;
                // cout << "writelog ";
                datamanager.writeLog(to[j], cap[j]);
                // cout << "writelog end\n";
            }
        }
    }
    // cout << "outans end\n";
}
void setGraph(double ratio){
    clear();
    for(int i=1;i<(int)graph.size();i++){
        int a = graph[i].first, b = graph[i].second;
        //if(a > b) std::swap(a, b);
        addEdge(a, b + siteNum, 0x3f3f3f3f);
        //cout <<"abab "<< a << " " << b << endl;
    }
    int demand_sum = 0;
    for(int i=1;i<=clientNum;i++){
        addEdge(siteNum + i, TARGET_PORT, demands[i]);
        demand_sum += demands[i];
    }
    for(int i=1;i<=siteNum; i++){
        addEdge(SOURCE_PORT, i, (int)(limits[i] * ratio));
    }
}
void solve(int ti){
    datamanager.getDemands(ti, demands);
    double L=0, R = 1, middle=0;
    int tmp = 20;
    while (tmp--) {
        middle = (L+R)/2;
        setGraph(middle);
        int demand_sum = 0;
        for(int i=1;i<=clientNum;i++)
            demand_sum += demands[i];
        int flow = dinic(SOURCE_PORT, TARGET_PORT);
        // cout << L << " " << R << " " << flow << " " << demand_sum << endl;
        if(flow == demand_sum){
            R = middle;
        }else{
            L = middle;
        }
    }
    setGraph(R);
    dinic(SOURCE_PORT, TARGET_PORT);
    // cout << "abab\n";
    outputAns(ti);
    // cout << "finished abab\n";
    clear();
}

void writeToFile(std::string fileName = "solution.txt")
{
#ifdef CMAKE
    datamanager.writeFile("../output/" + fileName);
#endif 
#ifdef VISUALSTUDIO
    datamanager.writeFile("../../../output/" + fileName);
#endif
#ifdef SUBMIT
    datamanager.writeFile("/output/" + fileName);
#endif
}

int main(){
    
    datamanager.getQosGraph(siteNum, clientNum, graph);

    for (auto& node : graph)
    {
        //std::cout << "(" << node.first << ", " << node.second << ")\n";
    }

    datamanager.getSiteLimits(limits);
    n = siteNum + clientNum + 2;
    SOURCE_PORT = n-1;
    TARGET_PORT = n;
    T = datamanager.getTotalTime();
    // cout << n << " " << T << endl;
    for(int i=1;i<=T;i++){
        // cout << i << endl;
        solve(i);
    }

    //writeToFile("solution1.txt");

    OptimizerFineTunning optFineTune(&datamanager);
    optFineTune.solve(100);
    optFineTune.writeBack();

    writeToFile();


    return 0;
}